<?php
/**
 * EasyGD - A simple class to handle images in PHP with GD
 *
 * @author Éber Freitas Dias <eusou@eberfdias.com>
 * @license http://www.gnu.org/copyleft/lgpl.html GNU Lesser General Public License 3
 * 
 * @package EasyGD
 * @version 0.4 - January 11th, 2009 - 01:08pm
 **/

/******************************/
/* GLOBALS FOR ERROR HANDLING */
/******************************/

define("ERROR_OPEN", "It wasn't possible to open the provided image.");
define("ERROR_SUPPORT", "This file format is not suported by EasyGD. Use a jpg or png image instead.");
define("ERROR_RESOURCE", "There is no resource for this function. Please, run EasyGD::startFrom* to define one.");
define("ERROR_CROP", "One of the dimensions provided are bigger than the actual image.");

class EasyGD {
	/**********************/
	/* GENERAL ATTRIBUTES */
	/**********************/
	
	/**
	 * @var array List of supported image types where key = extension and value = mime type
	 **/
	var $egdFileExts = array(
		'jpg' => 'image/jpeg',
		'png' => 'image/png'
	);
	
	/**
	 * @var array List of positions for cropping. The first letter represents the x axis (Left, Center, Right) and the second letter represents the y axis (Top, Center, Bottom)
	 **/
	var $egdCropTypes = array(
		'LT', 'CT', 'RT',
		'LC', 'CC', 'RC',
		'LB', 'CB', 'RB'
	);
	
	/**
	 * @var resource Variable where the current resource will be stored
	 **/
	var $egdResource = null;
	
	/**
	 * @var string Holds the original file name
	 **/
	var $egdFileName = null;
	
	/**
	 * @var string Extension from the current image being manipulated
	 **/
	var $egdExt      = null;
	
	/**
	 * @var int Width of the image
	 **/
	var $egdX        = null;
	
	/**
	 * @var int Height of the image
	 **/
	var $egdY        = null;
	
	/**
	 * @var array This variable holds a list of functions to be used by specific file types
	 **/
	var $egdFunction = null;
	
	/**
	 * @var int This stores the image quality (for JPG, from 0 to 100) and compression (for PNG, from 0 to 9)
	 **/
	var $egdQuality  = null;
	
	/**
	 * @var bool If your host doesn't allow you to access external url from PHP (like Dreamhost does), you might have to turn this on using EasyGD::startFromUrl
	 **/
	var $egdCurl     = false;
	
	/*******************/
	/* START FUNCTIONS */
	/*******************/

	/**
	 * Use this function to start EasyGD from a file.
	 * @param undefined $file This can receive a string with the filename Ex. 'image.jpg' or an array from the $_FILES global Ex. $_FILES['myfile']
	 * @return resource If everything goes ok, it returns the image resource and prepares the script to handle the image
	 **/
	function startFromFile($file) {
		if(!is_array($file)) {
			$ext = strtolower(substr(strrchr($file, "."), 1));
			
			if(!file_exists($file)) {
				$this->__error(ERROR_OPEN);
				return false;
			}
			
			if(!$this->__check_support(null, $ext)) {
				$this->__error(ERROR_SUPPORT);
				return false;
			}
			
			$this->egdFileName = $file;
			$this->egdExt = $ext;
		} else {
			if($file['error'] != 0) {
				$this->__error(ERROR_OPEN);
			}
			
			if(!$this->__check_support($file['type'])) {
				$this->__error(ERROR_SUPPORT);
				return false;
			}
			
			$toGetExt = array_flip($this->egdFileExts);
			
			$this->egdFileName = $file['name'];
			$this->egdExt = $toGetExt[$file['type']];
			$file = $file['tmp_name'];
		}
		
		$this->egdFunction = $this->__get_function($this->egdExt);
		$this->egdResource = call_user_func($this->egdFunction['create'], $file);
		list($this->egdX, $this->egdY) = getimagesize($file);
		
		return true;
	}
	
	/**
	 * With this function you can start EasyGD from an already started resource
	 * @param resource $resource The image resource to be used
	 * @param string $ext The original image extension
	 * @return resource If everything goes ok, it returns the image resource and prepares the script to handle the image
	 **/
	function startFromResource($resource, $ext, $filename = null) {
		if(!$this->__check_support(null, $ext)) {
			$this->__error(ERROR_SUPPORT);
			return false;
		}
		
		$this->egdResource = $resource;
		$this->egdFileName = !is_null($filename) ? $filename : '';
		$this->egdExt = $ext;
		$this->egdX = imagesx($resource);
		$this->egdY = imagesy($resource);
		$this->egdFunction = $this->__get_function($this->egdExt);
		
		return true;
	}
	
	/**
	 * With this function you can start EasyGD to use a remote image from a URL. If you need to use cURL, turn EasyGD::egdCurl = true;
	 * @url string The URL of the remote image
	 * @return resource If everything goes ok, it returns the image resource and prepares the script to handle the image
	 **/
	function startFromUrl($url) {
		$ext = strtolower(substr(strrchr($url, "."), 1));
		
		if(!$this->__check_support(null, $ext)) {
			$this->__error(ERROR_SUPPORT);
			return false;
		}
		
		if(!$this->egdCurl) {
			$img = file_get_contents($url);
		} else {
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
			$img = curl_exec($ch);
			curl_close($ch);
		}
		
		$this->egdResource = imagecreatefromstring($img) or $this->__error(ERROR_OPEN);
		$this->egdFileName = strtolower(substr(strrchr($url, "/"), 1));
		$this->egdExt = $ext;
		$this->egdX = imagesx($this->egdResource);
		$this->egdY = imagesy($this->egdResource);
		$this->egdFunction = $this->__get_function($this->egdExt);
		
		return true;
	}
	
	/******************/
	/* OUTPUT METHODS */
	/******************/
	
	/**
	 * This function displays the image on the browser directly
	 **/
	function display() {
		if(!$this->__check_resource()) {
			$this->__error(ERROR_RESOURCE);
			return false;
		}
		
		header('Content-Type: '.$this->egdFileExts[$this->egdExt]);
		if(call_user_func_array($this->egdFunction['display'], array($this->egdResource, null, $this->egdQuality))) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * This function saves the resource to a image file
	 * @param string $filename The image name to be used Ex. 'image.jpg'. Don't forget to insert the right extension. You can use EasyGD::egdExt to do that.
	 * @param string $path Optional param to hold the path where the image will be stored. It should finish with a "/"
	 **/
	function save($filename, $path = '') {
		if(!empty($path) && $path{strlen($path)-1} != "/") { $path = $path . "/"; }
		
		if(call_user_func_array($this->egdFunction['display'], array($this->egdResource, $path.$filename, $this->egdQuality))) {
			return true;
		}
		
		return false;
	}
	
	/************************/
	/* MANIPULATION METHODS */
	/************************/
	
	/**
	 * Function to resize an image
	 * @param int $width Width to be resized
	 * @param int $height Height to be resized
	 **/
	function resize($width, $height) {
		if(!$this->__check_resource()) {
			$this->__error(ERROR_RESOURCE);
			return false;
		}
		
		$newResource = imagecreatetruecolor($width, $height);
		imagecopyresampled($newResource, $this->egdResource, 0, 0, 0, 0, $width, $height, $this->egdX, $this->egdY);
		
		$this->startFromResource($newResource, $this->egdExt, $this->egdFileName);
		
		return true;
	}
	
	/**
	 * Shortcut for the EasyGD::resize function where you can set a with and the height is calculated to the proportion
	 * @param int $width New image width to be resized
	 **/
	function width($width) {
		$this->resize($width, round(($this->egdY / $this->egdX) * $width));
	}
	
	/**
	 * Shortcut for the EasyGD::resize function where you can set a height and the width is calculated to the proportion
	 * @param int $height New image height to be resized
	 **/
	function height($height) {
		$this->resize(round($this->egdX / $this->egdY) * $height, $height);
	}
	
	/**
	 * Function where you can crop an image. You can use the pre-defined patterns with $type or define your own position with $x and $y
	 * @param int $width Width of the crop
	 * @param int $height Height of the crop
	 * @param string $type One of the EasyGD::egdCropTypes
	 * @param int $x The X axis to start the crop
	 * @param int $y The Y axis to start the crop
	 */
	function crop($width, $height, $type = 'CC', $x = null, $y = null) {
		if(!$this->__check_resource()) {
			$this->__error(ERROR_RESOURCE);
			return false;
		}
		
		if($width > $this->egdX || $height > $this->egdY) {
			$this->__error(ERROR_CROP);
		}
		
		if(!in_array($type, $this->egdCropTypes) || is_null($type)) {
			$type = 'CC';
		}
		
		$leftX = $this->egdX - $width;
		$leftY = $this->egdY - $height;
		
		if(is_null($x) || is_null($y)) {
			switch($type) {
				case "LT":
					$x = $y = 0;
					break;
				case "CT":
					$x = round($leftX / 2); $y = 0;
					break;
				case "RT":
					$x = $leftX; $y = 0;
					break;
				case "LC":
					$x = 0; $y = round($leftY / 2);
					break;
				case "CC":
					$x = round($leftX / 2); $y = round($leftY / 2);
					break;
				case "RC":
					$x = $leftX; $y = round($leftY / 2);
					break;
				case "LB":
					$x = 0; $y = $leftY;
					break;
				case "CB":
					$x = round($leftX / 2); $y = $leftY;
					break;
				case "RB":
					$x = $leftX; $y = $leftY;
					break;
			}
		} else {
			if($x + $width > $this->egdX)
				$x = $leftX;
			if($y + $height > $this->egdY)
				$y = $leftY;
		}
		
		$newResource = imagecreatetruecolor($width, $height);
		imagecopy($newResource, $this->egdResource, 0, 0, $x, $y, $this->egdX, $this->egdY);
		
		$this->startFromResource($newResource, $this->egdExt, $this->egdFileName);
		
		return true;
	}
	
	/**
	 * This will destroy the current resource and reset the variables
	 **/
	function end() {
		return $this->__reset();
	}
	
	/********************/
	/* INTERNAL METHODS */
	/********************/
	
	function __reset() {
		if(!$this->__check_resource()) {
			return true;
		}
		
		imagedestroy($this->egdResource);
		$this->egdExt = null;
		$this->egdX = null;
		$this->egdY = null;
		$this->egdFunction = null;
		$this->egdQuality = null;
		
		return true;
	}
	
	function __check_resource() {
		return gettype($this->egdResource) == 'resource';
	}
	
	function __check_support($mime = null, $ext = null) {
		if(!is_null($mime)) {
			return in_array($mime, $this->egdFileExts);
		} elseif(!is_null($ext)) {
			$toCheck = array_flip($this->egdFileExts);
			return in_array($ext, $toCheck);
		}
		
		return false;
	}
	
	function __get_function($ext) {
		switch($ext) {
			case "jpg":
				$this->egdQuality = 100;
				return array(
					'create'	=> 'imagecreatefromjpeg',
					'display'	=> 'imagejpeg'
				);
				break;
			case "png":
				$this->egdQuality = 0;
				return array(
					'create'	=> 'imagecreatefrompng',
					'display'	=> 'imagepng'
				);
				break;
			default:
				$this->__error(ERROR_SUPPORT);
				return false;
		}
	}
	
	function __error($msg) {
		trigger_error($msg, E_USER_WARNING);
	}
}

?>